package mw

import (
	"context"
	"errors"
	"fmt"
	consts2 "github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/consts"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/dal/mysql"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/hertz_gen/user_manager"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/model"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/repo/wxapi"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/utils"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
	"github.com/hertz-contrib/jwt"
	"gorm.io/gorm"
	"time"
)

var (
	JwtMiddleware *jwt.HertzJWTMiddleware
	identity      = "user_id"
)

func InitJwt() {
	JwtMiddleware, _ = jwt.New(&jwt.HertzJWTMiddleware{
		Key: []byte("tiktok secret key"),
		//TokenLookup: "query:token,form:token",
		Timeout:     24 * time.Hour,
		IdentityKey: identity,
		// Verify password at login
		Authenticator: func(ctx context.Context, c *app.RequestContext) (interface{}, error) {
			var err error
			var req user_manager.LoginUserRequest
			err = c.BindAndValidate(&req)
			if err != nil {
				return nil, err
			}
			// 获取Code
			hlog.CtxInfof(ctx, "UserLogin code:%s", req.Code)
			session, err := wxapi.NewWxApiProxy().JsCode2session(ctx, wxapi.Jscode2sessionReq{
				Code: req.Code,
			})
			hlog.CtxInfof(ctx, "UserLogin session:%v", utils.QuickJSONMarshal(session))
			if session.ErrCode != 0 {
				return nil, errors.New(session.ErrMsg)
			}
			user, err := mysql.GetUserByUnionID(session.UnionId)
			if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
				hlog.CtxErrorf(ctx, "UserLogin create user error:%s", err.Error())
				return nil, err
			}
			// 存在用户
			if err == nil {
				consts2.SetUserInfoToCtx(c, user)
				return map[string]interface{}{"session": session.SessionKey, "openid": session.OpenId}, err
			}
			user, err = mysql.GetUserByOpenID(session.OpenId)
			if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
				hlog.CtxErrorf(ctx, "UserLogin create user error:%s", err.Error())
				return nil, err
			}
			if err == nil {
				//openid绑定的unionid错误
				user.WxUnionID = session.UnionId
				if err = mysql.DB.Save(user).Error; err != nil {
					hlog.CtxErrorf(ctx, "UserLogin create user error:%s", err.Error())
					return nil, err
				}
				consts2.SetUserInfoToCtx(c, user)
				return map[string]interface{}{"session": session.SessionKey, "openid": session.OpenId}, nil
			}
			//新用户
			user = &model.User{
				WxOpenID:  session.OpenId,
				WxUnionID: session.UnionId,
			}
			if err := mysql.CreateUser([]*model.User{user}); err != nil {
				hlog.CtxErrorf(ctx, "UserLogin create user error:%s", err.Error())
				return nil, err
			}
			consts2.SetUserInfoToCtx(c, user)
			return map[string]interface{}{"session": session.SessionKey, "openid": session.OpenId}, nil
		},
		// Set the payload in the token
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(map[string]interface{}); ok {
				return jwt.MapClaims{
					identity: v,
				}
			}
			return jwt.MapClaims{}
		},
		// build login response if verify password successfully
		LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
			hlog.CtxInfof(ctx, "Login success ，token is issued clientIP: "+c.ClientIP())
			consts2.SetTokenToCtx(c, token)
			c.Header(consts.HeaderAuthorization, token)
		},
		// Verify token and get the id of logged-in user
		Authorizator: func(data interface{}, ctx context.Context, c *app.RequestContext) bool {
			hlog.CtxInfof(ctx, "Authorization: %v", utils.QuickJSONMarshal(data))
			if v, ok := data.(map[string]interface{}); ok {
				session := v["session"].(string)
				openId := v["openid"].(string)
				checkSession, err := wxapi.NewWxApiProxy().CheckSession(ctx, &wxapi.CheckSessionReq{
					SessionKey: session,
					OpenId:     openId,
				}, 3)
				if err != nil {
					hlog.CtxErrorf(ctx, "CheckSession error:%s ErrCode %v ErrMsg %v", err,
						checkSession.ErrCode, checkSession.ErrMsg)
					return false
				}
				user, err := mysql.GetUserByOpenID(openId)
				if err != nil {
					hlog.CtxErrorf(ctx, "GetUserByOpenID error:%s", err.Error())
					return false
				}
				consts2.SetUserInfoToCtx(c, user)
				hlog.CtxInfof(ctx, "Token is verified clientIP: "+c.ClientIP())
				return true
			}
			return false
		},
		// Validation failed, build the message
		Unauthorized: func(ctx context.Context, c *app.RequestContext, code int, message string) {
			c.String(consts.StatusUnauthorized, fmt.Sprintf("%v(%v)", message, code))
		},
	})
}
